Hierarchie von Ausnahmen anzeigen
In einem Projekt stand ich erneut vor der Aufgabe, eine Hierarchie von ausgelösten Exceptions anzeigen zu müssen, um einem Fehler auf die Spur zu kommen. Natürlich kann man sich im Debugger das Ausnahmeobjekt anzeigen lassen und jeweils die PREVIOUS-Ausnahmeobjekte durchklicken. Allerdings funktioniert das nur, wenn man selbst den Fehler nachstellen kann. Einfacher wäre es unter Umständen, wenn der Anwender bereits die Aufrufhierarchie sehen könnte. Das könnten zwar durchaus zu viele und vor allen Dingen zu technische Informationen sein, aber in jedem Fall besser, wenn man dem Entwickler diesen Screenshot schicken kann, als nur die lapidare Meldung “Es ist eine Ausnahme aufgetreten”.
Da es im Standard anscheinend keinen Baustein gibt, um diese Aufrufhierarchie anzuzeigen – jedenfalls habe ich keinen gefunden –, habe ich kurzerhand selbst eine kleine Funktion geschrieben.
Worum geht es genau?
Klassenbasierte Ausnahmen können nach oben, also an den Aufrufer, propagiert werden. Dabei kann der Ausnahme immer der Parameter PREVIOUS übergeben werden. In diesem Parameter kann ein Ausnahmeobjekt eines vorherigen Fehlers übergeben werden. Das bietet die Möglichkeit, die komplette Fehlerhistorie nachvollziehen zu können.
Beispielprogramm
Das folgende Beispielprogramm soll das Vorgehen kurz und knapp demonstrieren. Ich habe hier lokal drei Ausnahmeklassen definiert:
- ERR_ST – erbt von CX_STATIC_CHECK und hat ein IF_T100-Interface
- ERR_DYN – erbt von CX_DYNAMIC_CHECK und hat ein IF_T100-Interface
- ERR_PURE – erbt von CX_STATIC_CHECK und hat kein Interface für MESSAGES
Die Klasse DEMO besteht aus den vier Methoden ONE, TWO, THREE und GO. GO ist die Startmethode und ruft ONE auf, die TWO aufruft, die THREE aufruft. Jede Methode benutzt dabei eine andere Ausnahmeklassen. Die vorher ausgelöste Ausnahme wird als PREVIOUS übergeben. Am Ende hat man eine Hierarchie von drei Ausnahmeinstanzen.
Code
REPORT. CLASS err_st DEFINITION INHERITING FROM cx_static_check CREATE PUBLIC. PUBLIC SECTION. INTERFACES if_t100_message. INTERFACES if_t100_dyn_msg. ENDCLASS. CLASS err_dyn DEFINITION INHERITING FROM cx_dynamic_check. PUBLIC SECTION. INTERFACES if_t100_message. INTERFACES if_t100_dyn_msg. ENDCLASS. CLASS err_pure DEFINITION INHERITING FROM cx_static_check. PUBLIC SECTION. DATA text TYPE string. METHODS constructor IMPORTING text TYPE clike OPTIONAL. methods get_text REDEFINITION. ENDCLASS. CLASS err_pure IMPLEMENTATION. METHOD constructor. super->constructor( ). me->text = text. ENDMETHOD. method get_text. result = me->text. ENDMETHOD. ENDCLASS. CLASS demo DEFINITION. PUBLIC SECTION. METHODS one RAISING err_st. METHODS two. METHODS three RAISING err_pure. METHODS go. ENDCLASS. CLASS demo IMPLEMENTATION. METHOD one. TRY. two( ). CATCH err_dyn INTO DATA(err). RAISE EXCEPTION TYPE err_st MESSAGE ID 'OO' TYPE 'E' NUMBER '000' WITH 'Method one failed' EXPORTING previous = err. ENDTRY. ENDMETHOD. METHOD two. TRY. three( ). CATCH cx_root INTO DATA(err). RAISE EXCEPTION TYPE err_dyn MESSAGE ID 'OO' TYPE 'E' NUMBER '000' WITH 'Failure method two' EXPORTING previous = err. ENDTRY. ENDMETHOD. METHOD three. data(err) = new err_pure( text = 'pure exception w/o T100 interface' ). RAISE EXCEPTION err. ENDMETHOD. METHOD go. TRY. one( ). CATCH cx_root INTO DATA(error). excp=>show( error ). ENDTRY. ENDMETHOD. ENDCLASS. START-OF-SELECTION. NEW demo( )->go( ).
Anzeige der Ausnahmen
Im oben gezeigten Coding wird die Methode EXCP=>SHOW aufgerufen. Diese stelle ich dir jetzt vor. Sie nimmt ein beliebiges Ausnahmeobjekt entgegen (vom Typ CX_ROOT oder Unterklassen) und sammelt Informationen über die Ausnahmehierarchie.
Mit Hilfe der Klasse CL_ABAP_CLASSDESCR wird der Name der Ausnahmeklasse ermittelt. Klasse CL_MESSAGE_HELPER hilft dabei, die MESSAGE-Variablen in die entsprechenden SY-Felder zu stellen. Die Ermittelten Daten werden mit Hilfe des CL_SALV_TABLE im Popup angezeigt.
Verbesserungen
Das hier vorgestellte Popup sollte einem Endanwender nicht direkt angezeigt werden, denn es werden sehr viele technische Details gezeigt. Sinnvoller wäre sicherlich eine Popup, dass eine entsprechende Nachricht oder die Meldung der letzten Ausnahme mit der Möglichkeit, die Aufrufhierarchie anzeigen zu lassen.
Meiner Meinung nach müsste diese Funktion bereits direkt in der SAPGUI beim Anzeigen einer Ausnahme-MESSAGE möglich sein, wenn die Meldung mittels MESSAGE exception_object TYPE ‘I’ ausgegeben wird.
Code
CLASS excp DEFINITION. PUBLIC SECTION. CLASS-METHODS show IMPORTING obj TYPE REF TO cx_root. PRIVATE SECTION. TYPES: BEGIN OF _exception, message TYPE string, msgno TYPE symsgno, msgid TYPE symsgid, msgv1 TYPE symsgv, msgv2 TYPE symsgv, msgv3 TYPE symsgv, msgv4 TYPE symsgv, relative_name TYPE string, absolute_name TYPE string, prog_name TYPE syrepid, incl_name TYPE inclname, source_line TYPE i, END OF _exception, _exceptions TYPE STANDARD TABLE OF _exception WITH DEFAULT KEY. CLASS-DATA exceptions TYPE _exceptions. CLASS-METHODS fill_table IMPORTING obj TYPE REF TO cx_root. CLASS-METHODS display_popup. ENDCLASS. CLASS excp IMPLEMENTATION. METHOD show. fill_table( obj ). display_popup( ). ENDMETHOD. METHOD fill_table. DATA(error) = CAST cx_root( obj ). WHILE error IS BOUND. APPEND INITIAL LINE TO exceptions ASSIGNING FIELD-SYMBOL(<excp>). DATA(descr) = cl_abap_classdescr=>describe_by_object_ref( error ). <excp>-absolute_name = descr->absolute_name. <excp>-relative_name = descr->get_relative_name( ). <excp>-message = error->get_text( ). error->get_source_position( IMPORTING program_name = <excp>-prog_name include_name = <excp>-incl_name source_line = <excp>-source_line ). IF error IS INSTANCE OF if_t100_message. DATA(msg) = CAST if_t100_message( error ). cl_message_helper=>set_msg_vars_for_if_t100_msg( msg ). <excp>-msgno = sy-msgno. <excp>-msgid = sy-msgid. <excp>-msgv1 = sy-msgv1. <excp>-msgv2 = sy-msgv2. <excp>-msgv3 = sy-msgv3. <excp>-msgv4 = sy-msgv4. ENDIF. error ?= error->previous. ENDWHILE. ENDMETHOD. METHOD display_popup. TRY. cl_salv_table=>factory( IMPORTING r_salv_table = DATA(popup) CHANGING t_table = exceptions ). popup->set_screen_popup( start_column = 10 end_column = 170 start_line = 2 end_line = 10 ). popup->get_columns( )->set_optimize( abap_true ). popup->get_columns( )->get_column( 'MESSAGE' )->set_medium_text( `Message` ) ##no_text. popup->get_columns( )->get_column( 'ABSOLUTE_NAME' )->set_medium_text( `Absolute class name` ) ##no_text. popup->get_columns( )->get_column( 'RELATIVE_NAME' )->set_medium_text( `Relative class name` ) ##no_text. popup->display( ). CATCH cx_salv_msg cx_salv_not_found. ENDTRY. ENDMETHOD. ENDCLASS.
- Meine Eclipse-Plugins - 22. November 2024
- Interview mit Björn Schulz (Software-Heroes.com) - 3. September 2024
- Daten aus ALV ermitteln - 3. September 2024